package moe.yakusoku.ll3thablumdraw.service;

import moe.yakusoku.ll3thablumdraw.bean.DrawParams;
import moe.yakusoku.ll3thablumdraw.bean.DrawResult;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * 抽奖逻辑
 * ———————————————————————————————————————————————————————
 * 权重解释:
 * 设：参与者"约定"只参与了南ことり一个角色的抽奖，"约定"在南ことり抽奖中权重为1。
 *
 * 则：参与者如果参与了N个角色抽奖（N > 3），则在所有参与的抽奖中权重均为1/N。故存在人品爆炸同时抽到好几个的可能。
 *
 * CP推BUFF：
 * 参与者如果参与了两个角色的抽奖，如妮姬，则在にこ和真姬的抽奖中权重均为1。
 * 参与者如果参与了三个角色的抽奖，如果海鸟，则在果海鸟三人的抽奖中权重均为1/2。
 * ———————————————————————————————————————————————————————
 * 来自黑箱的结果会标记"（Covert Deal）"。
 * 特别奖从所有参与者中抽选，所有人权重均为1。如果只参与了一个角色的抽奖且该角色被黑箱给其他人，则特别奖的权重为2。
 */
@Service
public class DrawService {
    @Value("${draw.honoka.list}")
    private String honokaStr;

    @Value("${draw.kotori.list}")
    private String kotoriStr;

    @Value("${draw.umi.list}")
    private String umiStr;

    @Value("${draw.maki.list}")
    private String makiStr;

    @Value("${draw.rin.list}")
    private String rinStr;

    @Value("${draw.hanayo.list}")
    private String hanayoStr;

    @Value("${draw.nico.list}")
    private String nicoStr;

    @Value("${draw.nozomi.list}")
    private String nozomiStr;

    @Value("${draw.eli.list}")
    private String eliStr;

    @Value("${draw.do.time}")
    private String doTime;

    @Autowired
    private Environment env;

    private List<String> loss2CovertDealNameList = new ArrayList<>();

    public DrawResult draw(){
        Map<String,DrawParams> actors = initActorsParams();

        DrawResult re = new DrawResult();
        re.setDrawTime(doTime);
        re.setHonoka(doDraw(makeNameList(honokaStr),actors,env.getProperty("draw.honoka.black")));
        re.setKotori(doDraw(makeNameList(kotoriStr),actors,env.getProperty("draw.kotori.black")));
        re.setUmi(doDraw(makeNameList(umiStr),actors,env.getProperty("draw.umi.black")));
        re.setMaki(doDraw(makeNameList(makiStr),actors,env.getProperty("draw.maki.black")));
        re.setRin(doDraw(makeNameList(rinStr),actors,env.getProperty("draw.rin.black")));
        re.setHanayo(doDraw(makeNameList(hanayoStr),actors,env.getProperty("draw.hanayo.black")));
        re.setNico(doDraw(makeNameList(nicoStr),actors,env.getProperty("draw.nico.black")));
        re.setNozomi(doDraw(makeNameList(nozomiStr),actors,env.getProperty("draw.nozomi.black")));
        re.setEli(doDraw(makeNameList(eliStr),actors,env.getProperty("draw.eli.black")));

        List<String> actorList = actors.entrySet().stream().map(e -> e.getKey()).collect(Collectors.toList());
        actorList = Stream.concat(actorList.stream(), loss2CovertDealNameList.stream()).collect(Collectors.toList());

        Map<String,String> special = new HashMap<>();
        special.put("I",actorList.get(new Random().nextInt(actorList.size())));

        re.setSpecial(special);

        re.setCreateDate(new Date());

        return re;
    }

    public DrawResult def(){
        String defRe = "还没开奖，别急哦！";

        DrawResult re = new DrawResult();
        re.setDrawTime(doTime);
        re.setHonoka(defRe);
        re.setKotori(defRe);
        re.setUmi(defRe);
        re.setMaki(defRe);
        re.setRin(defRe);
        re.setHanayo(defRe);
        re.setNico(defRe);
        re.setNozomi(defRe);
        re.setEli(defRe);

        re.setCreateDate(new Date());

        return re;
    }

    private Map<String,DrawParams> initActorsParams(){
        List<String> nameList = Stream.of((honokaStr + kotoriStr + umiStr + makiStr + rinStr + hanayoStr + nicoStr
                + nozomiStr + eliStr).split("@")).filter(str -> !str.trim().equals("")).map(str -> "@" + (str.trim())).collect(Collectors.toList());

        Map<String,DrawParams> actors = new HashMap<>();
        nameList.stream().distinct().forEach(str -> actors.put(str,new DrawParams()));

        nameList.forEach(str -> actors.get(str).setCount(actors.get(str).getCount() + 1));

        return actors;
    }

    private String doDraw(String[] nameList, Map<String,DrawParams> actorsParams,String black){
        if(!stringIsBlank(black)){
            for(String name : nameList){
                if(actorsParams.get(name).getCount() == 1 && !black.equals(name)){
                    loss2CovertDealNameList.add(name);
                }
            }
            return black + " （Covert Deal）";
        }

        List<String> drawList = new ArrayList<>();
        Arrays.stream(nameList).forEach(str -> {
            int count = actorsParams.get(str).getCount();
            if(count == 2) count = 1;
            if(count == 3) count = 2;

            int rate = itemPart() / count;

            for(int i = 0 ; i < rate ; i ++){
                drawList.add(str);
            }
        });
        return drawList.get(new Random().nextInt(drawList.size()));
    }

    private int itemPart(){
        Integer[] ints = new Integer[]{5,7,2,2,2,3,3};
        return Arrays.asList(ints).stream().reduce(1 ,(x , y) -> x * y);
    }

    private String[] makeNameList(String nameStr){
        return Stream.of(nameStr.split("@")).filter(str -> !str.trim().equals("")).map(str -> "@" + (str.trim())).distinct().toArray(String[]::new);
    }

    private static boolean stringIsBlank(String str){
        return null == str || str.length() == 0;
    }

}
